home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / common / despeckle.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-07-22  |  27.7 KB  |  1,131 lines

  1. /*
  2.  * "$Id: despeckle.c,v 1.34.2.3 2001/07/22 19:48:18 utx Exp $"
  3.  *
  4.  *   Despeckle (adaptive median) filter for The GIMP -- an image manipulation
  5.  *   program
  6.  *
  7.  *   Copyright 1997-1998 Michael Sweet (mike@easysw.com)
  8.  *
  9.  *   This program is free software; you can redistribute it and/or modify
  10.  *   it under the terms of the GNU General Public License as published by
  11.  *   the Free Software Foundation; either version 2 of the License, or
  12.  *   (at your option) any later version.
  13.  *
  14.  *   This program is distributed in the hope that it will be useful,
  15.  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  *   GNU General Public License for more details.
  18.  *
  19.  *   You should have received a copy of the GNU General Public License
  20.  *   along with this program; if not, write to the Free Software
  21.  *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  22.  *
  23.  * Contents:
  24.  *
  25.  *   main()                      - Main entry - just call gimp_main()...
  26.  *   query()                     - Respond to a plug-in query...
  27.  *   run()                       - Run the filter...
  28.  *   despeckle()                 - Despeckle an image using a median filter.
  29.  *   despeckle_dialog()          -  Popup a dialog window for the filter box size...
  30.  *   preview_init()              - Initialize the preview window...
  31.  *   preview_scroll_callback()   - Update the preview when a scrollbar is moved.
  32.  *   preview_update()            - Update the preview window.
  33.  *   preview_exit()              - Free all memory used by the preview window...
  34.  *   dialog_iscale_update()      - Update the value field using the scale.
  35.  *   dialog_adaptive_callback()  - Update the filter type...
  36.  *   dialog_recursive_callback() - Update the filter type...
  37.  *   dialog_ok_callback()        - Start the filter...
  38.  *
  39.  *
  40.  * Revision History:
  41.  *
  42.  *   See ChangeLog
  43.  */
  44.  
  45. #include "config.h"
  46.  
  47. #include <stdio.h>
  48. #include <stdlib.h>
  49. #include <string.h>
  50.  
  51. #include <gtk/gtk.h>
  52.  
  53. #include <libgimp/gimp.h>
  54. #include <libgimp/gimpui.h>
  55.  
  56. #include "libgimp/stdplugins-intl.h"
  57.  
  58.  
  59. /*
  60.  * Constants...
  61.  */
  62.  
  63. #define PLUG_IN_NAME     "plug_in_despeckle"
  64. #define PLUG_IN_VERSION     "1.3.2 - 17 May 1998"
  65. #define PREVIEW_SIZE     128
  66. #define SCALE_WIDTH     80
  67. #define ENTRY_WIDTH     40
  68. #define MAX_RADIUS     20
  69.  
  70. #define FILTER_ADAPTIVE     0x01
  71. #define FILTER_RECURSIVE 0x02
  72.  
  73. #define despeckle_radius (despeckle_vals[0])    /* Radius of filter */
  74. #define filter_type     (despeckle_vals[1])    /* Type of filter */
  75. #define black_level     (despeckle_vals[2])    /* Black level */
  76. #define white_level     (despeckle_vals[3])    /* White level */
  77.  
  78. /*
  79.  * Local functions...
  80.  */
  81.  
  82. static void    query (void);
  83. static void    run   (gchar   *name,
  84.                gint     nparams,
  85.                GimpParam  *param,
  86.                gint    *nreturn_vals,
  87.                GimpParam **return_vals);
  88.  
  89. static void    despeckle (void);
  90.  
  91. static gint    despeckle_dialog          (void);
  92.  
  93. static void    dialog_iscale_update      (GtkAdjustment *, gint *);
  94. static void    dialog_adaptive_callback  (GtkWidget *, gpointer);
  95. static void    dialog_recursive_callback (GtkWidget *, gpointer);
  96. static void    dialog_ok_callback        (GtkWidget *, gpointer);
  97.  
  98. static void    preview_init              (void);
  99. static void    preview_exit              (void);
  100. static void    preview_update            (void);
  101. static void    preview_scroll_callback   (void);
  102.  
  103.  
  104. /*
  105.  * Globals...
  106.  */
  107.  
  108. GimpPlugInInfo PLUG_IN_INFO =
  109. {
  110.   NULL,  /* init  */
  111.   NULL,  /* quit  */
  112.   query, /* query */
  113.   run    /* run   */
  114. };
  115.  
  116. GtkWidget      *preview;        /* Preview widget */
  117. gint        preview_width,        /* Width of preview widget */
  118.         preview_height,        /* Height of preview widget */
  119.         preview_x1,        /* Upper-left X of preview */
  120.         preview_y1,        /* Upper-left Y of preview */
  121.         preview_x2,        /* Lower-right X of preview */
  122.         preview_y2;        /* Lower-right Y of preview */
  123. guchar           *preview_src = NULL,    /* Source pixel rows */
  124.            *preview_dst,        /* Destination pixel row */
  125.            *preview_sort;        /* Pixel value sort array */
  126. GtkObject      *hscroll_data,        /* Horizontal scrollbar data */
  127.            *vscroll_data;        /* Vertical scrollbar data */
  128.  
  129. GimpDrawable      *drawable = NULL;    /* Current image */
  130. gint        sel_x1,            /* Selection bounds */
  131.         sel_y1,
  132.         sel_x2,
  133.         sel_y2;
  134. gint        sel_width,        /* Selection width */
  135.         sel_height;        /* Selection height */
  136. gint        img_bpp;        /* Bytes-per-pixel in image */
  137.  
  138. gint run_filter = FALSE;    /* True if we should run the filter */
  139.  
  140. gint despeckle_vals[4] =
  141. {
  142.   3,
  143.   FILTER_ADAPTIVE,
  144.   7,
  145.   248
  146. };
  147.  
  148.  
  149. /*
  150.  * 'main()' - Main entry - just call gimp_main()...
  151.  */
  152.  
  153. MAIN ()
  154.  
  155. /*
  156.  * 'query()' - Respond to a plug-in query...
  157.  */
  158.  
  159. static void
  160. query (void)
  161. {
  162.   static GimpParamDef    args[] =
  163.   {
  164.     { GIMP_PDB_INT32,    "run_mode",    "Interactive, non-interactive" },
  165.     { GIMP_PDB_IMAGE,    "image",    "Input image" },
  166.     { GIMP_PDB_DRAWABLE,    "drawable",    "Input drawable" },
  167.     { GIMP_PDB_INT32,    "radius",    "Filter box radius (default = 3)" },
  168.     { GIMP_PDB_INT32,    "type",        "Filter type (0 = median, 1 = adaptive, 2 = recursive-median, 3 = recursive-adaptive)" },
  169.     { GIMP_PDB_INT32,    "black",    "Black level (-1 to 255)" },
  170.     { GIMP_PDB_INT32,    "white",    "White level (0 to 256)" }
  171.   };
  172.   static gint nargs = sizeof (args) / sizeof (args[0]);
  173.  
  174.   gimp_install_procedure (PLUG_IN_NAME,
  175.               "Despeckle filter, typically used to \'despeckle\' "
  176.               "a photographic image.",
  177.               "This plug-in selectively performs a median or "
  178.               "adaptive box filter on an image.",
  179.               "Michael Sweet <mike@easysw.com>",
  180.               "Copyright 1997-1998 by Michael Sweet",
  181.               PLUG_IN_VERSION,
  182.               N_("<Image>/Filters/Enhance/Despeckle..."),
  183.               "RGB*, GRAY*",
  184.               GIMP_PLUGIN,
  185.               nargs, 0,
  186.               args, NULL);
  187. }
  188.  
  189.  
  190. /*
  191.  * 'run()' - Run the filter...
  192.  */
  193.  
  194. static void
  195. run (gchar   *name,        /* I - Name of filter program. */
  196.      gint     nparams,        /* I - Number of parameters passed in */
  197.      GimpParam  *param,        /* I - Parameter values */
  198.      gint    *nreturn_vals,    /* O - Number of return values */
  199.      GimpParam **return_vals)    /* O - Return values */
  200. {
  201.   GimpRunModeType    run_mode;    /* Current run mode */
  202.   GimpPDBStatusType    status;        /* Return status */
  203.   GimpParam    *values;    /* Return values */
  204.  
  205.   /*
  206.    * Initialize parameter data...
  207.    */
  208.  
  209.   status   = GIMP_PDB_SUCCESS;
  210.   run_mode = param[0].data.d_int32;
  211.  
  212.   values = g_new (GimpParam, 1);
  213.  
  214.   values[0].type          = GIMP_PDB_STATUS;
  215.   values[0].data.d_status = status;
  216.  
  217.   *nreturn_vals = 1;
  218.   *return_vals  = values;
  219.  
  220.   /*
  221.    * Get drawable information...
  222.    */
  223.  
  224.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  225.  
  226.   gimp_drawable_mask_bounds (drawable->id, &sel_x1, &sel_y1, &sel_x2, &sel_y2);
  227.  
  228.   sel_width  = sel_x2 - sel_x1;
  229.   sel_height = sel_y2 - sel_y1;
  230.   img_bpp    = gimp_drawable_bpp (drawable->id);
  231.  
  232.   /*
  233.    * See how we will run
  234.    */
  235.  
  236.   switch (run_mode)
  237.     {
  238.     case GIMP_RUN_INTERACTIVE :
  239.       INIT_I18N_UI();
  240.       /*
  241.        * Possibly retrieve data...
  242.        */
  243.  
  244.       gimp_get_data (PLUG_IN_NAME, &despeckle_radius);
  245.  
  246.       /*
  247.        * Get information from the dialog...
  248.        */
  249.  
  250.       if (!despeckle_dialog())
  251.     return;
  252.       break;
  253.  
  254.     case GIMP_RUN_NONINTERACTIVE:
  255.       /*
  256.        * Make sure all the arguments are present...
  257.        */
  258.  
  259.       INIT_I18N();
  260.       if (nparams < 4 || nparams > 7)
  261.     status = GIMP_PDB_CALLING_ERROR;
  262.       else if (nparams == 4)
  263.     {
  264.       despeckle_radius = param[3].data.d_int32;
  265.       filter_type      = FILTER_ADAPTIVE;
  266.       black_level      = 7;
  267.       white_level      = 248;
  268.     }
  269.       else if (nparams == 5)
  270.     {
  271.       despeckle_radius = param[3].data.d_int32;
  272.       filter_type      = param[4].data.d_int32;
  273.       black_level      = 7;
  274.       white_level      = 248;
  275.     }
  276.       else if (nparams == 6)
  277.     {
  278.       despeckle_radius = param[3].data.d_int32;
  279.       filter_type      = param[4].data.d_int32;
  280.       black_level      = param[5].data.d_int32;
  281.       white_level      = 248;
  282.     }
  283.       else
  284.     {
  285.       despeckle_radius = param[3].data.d_int32;
  286.       filter_type      = param[4].data.d_int32;
  287.       black_level      = param[5].data.d_int32;
  288.       white_level      = param[6].data.d_int32;
  289.     };
  290.       break;
  291.  
  292.     case GIMP_RUN_WITH_LAST_VALS:
  293.       /*
  294.        * Possibly retrieve data...
  295.        */
  296.  
  297.       INIT_I18N();
  298.       gimp_get_data (PLUG_IN_NAME, despeckle_vals);
  299.     break;
  300.     
  301.     default:
  302.       status = GIMP_PDB_CALLING_ERROR;
  303.       break;;
  304.     };
  305.  
  306.   /*
  307.    * Despeckle the image...
  308.    */
  309.  
  310.   if (status == GIMP_PDB_SUCCESS)
  311.     {
  312.       if ((gimp_drawable_is_rgb(drawable->id) ||
  313.        gimp_drawable_is_gray(drawable->id)))
  314.     {
  315.       /*
  316.        * Set the tile cache size...
  317.        */
  318.  
  319.       gimp_tile_cache_ntiles (2 * (drawable->width + gimp_tile_width() - 1) /
  320.                   gimp_tile_width() + 1);
  321.  
  322.       /*
  323.        * Run!
  324.        */
  325.  
  326.       despeckle ();
  327.  
  328.       /*
  329.        * If run mode is interactive, flush displays...
  330.        */
  331.  
  332.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  333.         gimp_displays_flush ();
  334.  
  335.       /*
  336.        * Store data...
  337.        */
  338.  
  339.       if (run_mode == GIMP_RUN_INTERACTIVE)
  340.         gimp_set_data (PLUG_IN_NAME,
  341.                despeckle_vals, sizeof (despeckle_vals));
  342.     }
  343.       else
  344.     status = GIMP_PDB_EXECUTION_ERROR;
  345.     };
  346.  
  347.   /*
  348.    * Reset the current run status...
  349.    */
  350.  
  351.   values[0].data.d_status = status;
  352.  
  353.   /*
  354.    * Detach from the drawable...
  355.    */
  356.  
  357.   gimp_drawable_detach (drawable);
  358. }
  359.  
  360.  
  361. /*
  362.  * 'despeckle()' - Despeckle an image using a median filter.
  363.  *
  364.  * A median filter basically collects pixel values in a region around the
  365.  * target pixel, sorts them, and uses the median value. This code uses a
  366.  * circular row buffer to improve performance.
  367.  *
  368.  * The adaptive filter is based on the median filter but analizes the histogram
  369.  * of the region around the target pixel and adjusts the despeckle radius
  370.  * accordingly.
  371.  */
  372.  
  373. static void
  374. despeckle (void)
  375. {
  376.   GimpPixelRgn    src_rgn,    /* Source image region */
  377.         dst_rgn;    /* Destination image region */
  378.   guchar      **src_rows,    /* Source pixel rows */
  379.            *dst_row,    /* Destination pixel row */
  380.            *src_ptr,    /* Source pixel pointer */
  381.            *sort,        /* Pixel value sort array */
  382.            *sort_ptr;    /* Current sort value */
  383.   gint        sort_count,    /* Number of soft values */
  384.         i, j, t, d,    /* Looping vars */
  385.         x, y,        /* Current location in image */
  386.         row,        /* Current row in src_rows */
  387.         rowcount,    /* Number of rows loaded */
  388.         lasty,        /* Last row loaded in src_rows */
  389.         trow,        /* Looping var */
  390.         startrow,    /* Starting row for loop */
  391.         endrow,        /* Ending row for loop */
  392.         max_row,    /* Maximum number of filled src_rows */
  393.         size,        /* Width/height of the filter box */
  394.         width,        /* Byte width of the image */
  395.         xmin, xmax, tx,    /* Looping vars */
  396.         radius,        /* Current radius */
  397.         hist0,        /* Histogram count for 0 values */
  398.         hist255;    /* Histogram count for 255 values */
  399.  
  400.   /*
  401.    * Let the user know what we're doing...
  402.    */
  403.  
  404.   gimp_progress_init (_("Despeckling..."));
  405.  
  406.   /*
  407.    * Setup for filter...
  408.    */
  409.  
  410.   gimp_pixel_rgn_init (&src_rgn, drawable, sel_x1, sel_y1, sel_width, sel_height,
  411.                FALSE, FALSE);
  412.   gimp_pixel_rgn_init (&dst_rgn, drawable, sel_x1, sel_y1, sel_width, sel_height,
  413.                TRUE, TRUE);
  414.  
  415.   size     = despeckle_radius * 2 + 1;
  416.   max_row  = 2 * gimp_tile_height ();
  417.   width    = sel_width * img_bpp;
  418.  
  419.   src_rows    = g_new (guchar *, max_row);
  420.   src_rows[0] = g_new (guchar, max_row * width);
  421.  
  422.   for (row = 1; row < max_row; row ++)
  423.     src_rows[row] = src_rows[0] + row * width;
  424.  
  425.   dst_row = g_new (guchar, width),
  426.   sort    = g_new (guchar, size * size);
  427.  
  428.   /*
  429.    * Pre-load the first "size" rows for the filter...
  430.    */
  431.  
  432.   if (sel_height < gimp_tile_height())
  433.     rowcount = sel_height;
  434.   else
  435.     rowcount = gimp_tile_height ();
  436.  
  437.   gimp_pixel_rgn_get_rect (&src_rgn, src_rows[0], sel_x1, sel_y1, sel_width,
  438.                rowcount);
  439.  
  440.   row   = rowcount;
  441.   lasty = sel_y1 + rowcount;
  442.  
  443.   /*
  444.    * Despeckle...
  445.    */
  446.  
  447.   for (y = sel_y1 ; y < sel_y2; y ++)
  448.     {
  449.       if ((y + despeckle_radius) >= lasty &&
  450.       lasty < sel_y2)
  451.     {
  452.       /*
  453.        * Load the next block of rows...
  454.        */
  455.  
  456.       rowcount -= gimp_tile_height();
  457.       if ((i = sel_y2 - lasty) > gimp_tile_height())
  458.         i = gimp_tile_height();
  459.  
  460.       gimp_pixel_rgn_get_rect (&src_rgn, src_rows[row],
  461.                    sel_x1, lasty, sel_width, i);
  462.  
  463.       rowcount += i;
  464.       lasty    += i;
  465.       row      = (row + i) % max_row;
  466.     };
  467.  
  468.       /*
  469.        * Now find the median pixels and save the results...
  470.        */
  471.  
  472.       radius = despeckle_radius;
  473.  
  474.       memcpy (dst_row, src_rows[(row + y - lasty + max_row) % max_row], width);
  475.  
  476.       if (y >= (sel_y1 + radius) && y < (sel_y2 - radius))
  477.     {
  478.       for (x = 0; x < width; x ++)
  479.         {
  480.           hist0   = 0;
  481.           hist255 = 0;
  482.           xmin    = x - radius * img_bpp;
  483.           xmax    = x + (radius + 1) * img_bpp;
  484.  
  485.           if (xmin < 0)
  486.         xmin = x % img_bpp;
  487.  
  488.           if (xmax > width)
  489.         xmax = width;
  490.  
  491.           startrow = (row + y - lasty - radius + max_row) % max_row;
  492.           endrow   = (row + y - lasty + radius + 1 + max_row) % max_row;
  493.  
  494.           for (sort_ptr = sort, trow = startrow;
  495.            trow != endrow;
  496.            trow = (trow + 1) % max_row)
  497.         for (tx = xmin, src_ptr = src_rows[trow] + xmin;
  498.              tx < xmax;
  499.              tx += img_bpp, src_ptr += img_bpp)
  500.           {
  501.             if ((*sort_ptr = *src_ptr) <= black_level)
  502.               hist0 ++;
  503.             else if (*sort_ptr >= white_level)
  504.               hist255 ++;
  505.  
  506.             if (*sort_ptr < white_level && *sort_ptr > black_level)
  507.               sort_ptr ++;
  508.           };
  509.  
  510.           /*
  511.            * Shell sort the color values...
  512.            */
  513.  
  514.           sort_count = sort_ptr - sort;
  515.  
  516.           if (sort_count > 1)
  517.         {
  518.           for (d = sort_count / 2; d > 0; d = d / 2)
  519.             for (i = d; i < sort_count; i ++)
  520.               for (j = i - d, sort_ptr = sort + j;
  521.                j >= 0 && sort_ptr[0] > sort_ptr[d];
  522.                j -= d, sort_ptr -= d)
  523.             {
  524.               t           = sort_ptr[0];
  525.               sort_ptr[0] = sort_ptr[d];
  526.               sort_ptr[d] = t;
  527.             };
  528.  
  529.           /*
  530.            * Assign the median value...
  531.            */
  532.  
  533.           t = sort_count / 2;
  534.  
  535.           if (sort_count & 1)
  536.             dst_row[x] = (sort[t] + sort[t + 1]) / 2;
  537.           else
  538.             dst_row[x] = sort[t];
  539.  
  540.           /*
  541.            * Save the change to the source image too if the user
  542.            * wants the recursive method...
  543.            */
  544.  
  545.           if (filter_type & FILTER_RECURSIVE)
  546.             src_rows[(row + y - lasty + max_row) % max_row][x] =
  547.               dst_row[x];
  548.         };
  549.  
  550.           /*
  551.            * Check the histogram and adjust the radius accordingly...
  552.            */
  553.  
  554.           if (filter_type & FILTER_ADAPTIVE)
  555.         {
  556.           if (hist0 >= radius || hist255 >= radius)
  557.             {
  558.               if (radius < despeckle_radius)
  559.             radius ++;
  560.             }
  561.           else if (radius > 1)
  562.             radius --;
  563.         };
  564.         };
  565.     };
  566.  
  567.       gimp_pixel_rgn_set_row (&dst_rgn, dst_row, sel_x1, y, sel_width);
  568.  
  569.       if ((y & 15) == 0)
  570.     gimp_progress_update ((double) (y - sel_y1) / (double) sel_height);
  571.     };
  572.  
  573.   /*
  574.    * OK, we're done.  Free all memory used...
  575.    */
  576.  
  577.   g_free (src_rows[0]);
  578.   g_free (src_rows);
  579.   g_free (dst_row);
  580.   g_free (sort);
  581.  
  582.   /*
  583.    * Update the screen...
  584.    */
  585.  
  586.   gimp_drawable_flush (drawable);
  587.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  588.   gimp_drawable_update (drawable->id, sel_x1, sel_y1, sel_width, sel_height);
  589. }
  590.  
  591.  
  592. /*
  593.  * 'despeckle_dialog()' - Popup a dialog window for the filter box size...
  594.  */
  595.  
  596. static gint
  597. despeckle_dialog (void)
  598. {
  599.   GtkWidget *dialog;
  600.   GtkWidget *main_vbox;
  601.   GtkWidget *hbox;
  602.   GtkWidget *vbox;
  603.   GtkWidget *table;
  604.   GtkWidget *ptable;
  605.   GtkWidget *frame;
  606.   GtkWidget *scrollbar;
  607.   GtkWidget *button;
  608.   GtkObject *adj;
  609.   gchar *plugin_name;
  610.  
  611.   gimp_ui_init ("despeckle", TRUE);
  612.  
  613.   plugin_name = g_strdup_printf ("%s %s", _("Despeckle"), PLUG_IN_VERSION);
  614.  
  615.   dialog = gimp_dialog_new (plugin_name, "despeckle",
  616.                 gimp_standard_help_func, "filters/despeckle.html",
  617.                 GTK_WIN_POS_MOUSE,
  618.                 FALSE, TRUE, FALSE,
  619.  
  620.                 _("OK"), dialog_ok_callback,
  621.                 NULL, NULL, NULL, TRUE, FALSE,
  622.                 _("Cancel"), gtk_widget_destroy,
  623.                 NULL, 1, NULL, FALSE, TRUE,
  624.  
  625.                 NULL);
  626.  
  627.   g_free (plugin_name);
  628.  
  629.   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
  630.               GTK_SIGNAL_FUNC (gtk_main_quit),
  631.               NULL);
  632.  
  633.   main_vbox = gtk_vbox_new (FALSE, 4);
  634.   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
  635.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), main_vbox,
  636.               TRUE, TRUE, 0);
  637.   gtk_widget_show (main_vbox);
  638.  
  639.   hbox = gtk_hbox_new (FALSE, 4);
  640.   gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
  641.   gtk_widget_show (hbox);
  642.  
  643.   /*
  644.    * Preview window...
  645.    */
  646.  
  647.   ptable = gtk_table_new (2, 2, FALSE);
  648.   gtk_box_pack_start (GTK_BOX (hbox), ptable, FALSE, FALSE, 0);
  649.   gtk_widget_show(ptable);
  650.  
  651.   frame = gtk_frame_new (NULL);
  652.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  653.   gtk_table_attach (GTK_TABLE (ptable), frame, 0, 1, 0, 1, 0, 0, 0, 0);
  654.   gtk_widget_show (frame);
  655.  
  656.   preview_width  = MIN (sel_width, PREVIEW_SIZE);
  657.   preview_height = MIN (sel_height, PREVIEW_SIZE);
  658.  
  659.   preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  660.   gtk_preview_size (GTK_PREVIEW (preview), preview_width, preview_height);
  661.   gtk_container_add (GTK_CONTAINER (frame), preview);
  662.   gtk_widget_show (preview);
  663.  
  664.   hscroll_data = gtk_adjustment_new (0, 0, sel_width - 1, 1.0,
  665.                      MIN (preview_width, sel_width),
  666.                      MIN (preview_width, sel_width));
  667.  
  668.   gtk_signal_connect (hscroll_data, "value_changed",
  669.               GTK_SIGNAL_FUNC (preview_scroll_callback),
  670.               NULL);
  671.  
  672.   scrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT(hscroll_data));
  673.   gtk_range_set_update_policy (GTK_RANGE (scrollbar), GTK_UPDATE_CONTINUOUS);
  674.   gtk_table_attach (GTK_TABLE(ptable), scrollbar, 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
  675.   gtk_widget_show (scrollbar);
  676.  
  677.   vscroll_data = gtk_adjustment_new (0, 0, sel_height - 1, 1.0,
  678.                      MIN (preview_height, sel_height),
  679.                      MIN (preview_height, sel_height));
  680.  
  681.   gtk_signal_connect (vscroll_data, "value_changed",
  682.               GTK_SIGNAL_FUNC (preview_scroll_callback),
  683.               NULL);
  684.  
  685.   scrollbar = gtk_vscrollbar_new (GTK_ADJUSTMENT (vscroll_data));
  686.   gtk_range_set_update_policy (GTK_RANGE (scrollbar), GTK_UPDATE_CONTINUOUS);
  687.   gtk_table_attach (GTK_TABLE (ptable), scrollbar, 1, 2, 0, 1, 0,
  688.             GTK_FILL, 0, 0);
  689.   gtk_widget_show (scrollbar);
  690.  
  691.   preview_init ();
  692.  
  693.   preview_x1 = sel_x1;
  694.   preview_y1 = sel_y1;
  695.   preview_x2 = preview_x1 + MIN (preview_width, sel_width);
  696.   preview_y2 = preview_y1 + MIN (preview_height, sel_height);
  697.  
  698.   /*
  699.    * Filter type controls...
  700.    */
  701.  
  702.   frame = gtk_frame_new (_("Type"));
  703.   gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
  704.   gtk_widget_show (frame);
  705.  
  706.   vbox = gtk_vbox_new (FALSE, 2);
  707.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
  708.   gtk_container_add (GTK_CONTAINER (frame), vbox);
  709.   gtk_widget_show (vbox);
  710.  
  711.   button = gtk_check_button_new_with_label (_("Adaptive"));
  712.   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
  713.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
  714.                 (filter_type & FILTER_ADAPTIVE) ? TRUE : FALSE);
  715.   gtk_signal_connect (GTK_OBJECT (button), "toggled",
  716.               GTK_SIGNAL_FUNC (dialog_adaptive_callback),
  717.               NULL);
  718.   gtk_widget_show (button);
  719.  
  720.   button = gtk_check_button_new_with_label (_("Recursive"));
  721.   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
  722.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
  723.                 (filter_type & FILTER_RECURSIVE) ? TRUE : FALSE);
  724.   gtk_signal_connect (GTK_OBJECT (button), "toggled",
  725.               GTK_SIGNAL_FUNC (dialog_recursive_callback),
  726.               NULL);
  727.   gtk_widget_show (button);
  728.  
  729.   frame = gtk_frame_new (_("Parameter Settings"));
  730.   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  731.   gtk_widget_show (frame);
  732.  
  733.   table = gtk_table_new (3, 3, FALSE);
  734.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  735.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  736.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  737.   gtk_container_add (GTK_CONTAINER (frame), table);
  738.   gtk_widget_show (table);
  739.  
  740.   /*
  741.    * Box size (radius) control...
  742.    */
  743.  
  744.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  745.                   _("Radius:"), 100, 0,
  746.                   despeckle_radius, 1, MAX_RADIUS, 1, 5, 0,
  747.                   TRUE, 0, 0,
  748.                   NULL, NULL);
  749.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  750.               GTK_SIGNAL_FUNC (dialog_iscale_update),
  751.               &despeckle_radius);
  752.  
  753.   /*
  754.    * Black level control...
  755.    */
  756.  
  757.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
  758.                   _("Black Level:"), 100, 0,
  759.                   black_level, -1, 255, 1, 8, 0,
  760.                   TRUE, 0, 0,
  761.                   NULL, NULL);
  762.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  763.               GTK_SIGNAL_FUNC (dialog_iscale_update),
  764.               &black_level);
  765.  
  766.   /*
  767.    * White level control...
  768.    */
  769.  
  770.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
  771.                   _("White Level:"), 100, 0,
  772.                   white_level, 0, 256, 1, 8, 0,
  773.                   TRUE, 0, 0,
  774.                   NULL, NULL);
  775.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  776.               GTK_SIGNAL_FUNC (dialog_iscale_update),
  777.               &white_level);
  778.  
  779.   /*
  780.    * Show it and wait for the user to do something...
  781.    */
  782.  
  783.   gtk_widget_show (dialog);
  784.  
  785.   preview_update ();
  786.  
  787.   gtk_main ();
  788.   gdk_flush ();
  789.  
  790.   /*
  791.    * Free the preview data...
  792.    */
  793.  
  794.   preview_exit ();
  795.  
  796.   /*
  797.    * Return ok/cancel...
  798.    */
  799.  
  800.   return run_filter;
  801. }
  802.  
  803.  
  804. /*
  805.  * 'preview_init()' - Initialize the preview window...
  806.  */
  807.  
  808. static void
  809. preview_init (void)
  810. {
  811.   gint    size,        /* Size of filter box */
  812.     width;        /* Byte width of the image */
  813.  
  814.   /*
  815.    * Setup for preview filter...
  816.    */
  817.  
  818.   size  = despeckle_radius * 2 + 1;
  819.   width = preview_width * img_bpp;
  820.  
  821.   if (preview_src != NULL)
  822.     {
  823.       g_free (preview_src);
  824.       g_free (preview_dst);
  825.       g_free (preview_sort);
  826.     }
  827.  
  828.   preview_src  = (guchar *) g_new (guchar *, width * preview_height);
  829.   preview_dst  = g_new (guchar, width);
  830.   preview_sort = g_new (guchar, size * size);
  831. }
  832.  
  833.  
  834. /*
  835.  * 'preview_scroll_callback()' - Update the preview when a scrollbar is moved.
  836.  */
  837.  
  838. static void
  839. preview_scroll_callback (void)
  840. {
  841.   preview_x1 = sel_x1 + GTK_ADJUSTMENT (hscroll_data)->value;
  842.   preview_y1 = sel_y1 + GTK_ADJUSTMENT (vscroll_data)->value;
  843.   preview_x2 = preview_x1 + MIN (preview_width, sel_width);
  844.   preview_y2 = preview_y1 + MIN (preview_height, sel_height);
  845.  
  846.   preview_update ();
  847. }
  848.  
  849.  
  850. /*
  851.  * 'preview_update()' - Update the preview window.
  852.  */
  853.  
  854. static void
  855. preview_update (void)
  856. {
  857.   GimpPixelRgn    src_rgn;    /* Source image region */
  858.   guchar       *sort_ptr,    /* Current preview_sort value */
  859.            *src_ptr,    /* Current source pixel */
  860.            *dst_ptr;    /* Current destination pixel */
  861.   gint        sort_count,    /* Number of soft values */
  862.         i, j, t, d,    /* Looping vars */
  863.         x, y,        /* Current location in image */
  864.         size,        /* Width/height of the filter box */
  865.         width,        /* Byte width of the image */
  866.         xmin, xmax, tx,    /* Looping vars */
  867.         radius,        /* Current radius */
  868.         hist0,        /* Histogram count for 0 values */
  869.         hist255;    /* Histogram count for 255 values */
  870.   guchar    check,        /* Checkerboard pattern */
  871.         rgb[PREVIEW_SIZE * PREVIEW_SIZE * 3],
  872.                 /* Output image */
  873.            *rgb_ptr;    /* Pixel pointer for output */
  874.  
  875.  /*
  876.   * Setup for filter...
  877.   */
  878.  
  879.   gimp_pixel_rgn_init (&src_rgn, drawable,
  880.                preview_x1, preview_y1, preview_width, preview_height,
  881.                FALSE, FALSE);
  882.  
  883.   /*
  884.    * Pre-load the preview rectangle...
  885.    */
  886.  
  887.   size  = despeckle_radius * 2 + 1;
  888.   width = preview_width * img_bpp;
  889.  
  890.   gimp_pixel_rgn_get_rect (&src_rgn, preview_src, preview_x1, preview_y1,
  891.                preview_width, preview_height);
  892.  
  893.   /*
  894.    * Despeckle...
  895.    */
  896.  
  897.   for (y = 0; y < preview_height; y ++)
  898.     {
  899.       /*
  900.        * Now find the median pixels and save the results...
  901.        */
  902.  
  903.       radius = despeckle_radius;
  904.  
  905.       memcpy (preview_dst, preview_src + y * width, width);
  906.  
  907.       if (y >= radius && y < (preview_height - radius))
  908.     {
  909.       for (x = 0, dst_ptr = preview_dst; x < width; x ++, dst_ptr ++)
  910.         {
  911.           hist0   = 0;
  912.           hist255 = 0;
  913.           xmin    = x - radius * img_bpp;
  914.           xmax    = x + (radius + 1) * img_bpp;
  915.  
  916.           if (xmin < 0)
  917.         xmin = x % img_bpp;
  918.  
  919.           if (xmax > width)
  920.         xmax = width;
  921.  
  922.           for (i = -radius, sort_ptr = preview_sort,
  923.              src_ptr = preview_src + width * (y - radius);
  924.            i <= radius;
  925.            i ++, src_ptr += width)
  926.         for (tx = xmin; tx < xmax; tx += img_bpp)
  927.           {
  928.             if ((*sort_ptr = src_ptr[tx]) <= black_level)
  929.               hist0 ++;
  930.             else if (*sort_ptr >= white_level)
  931.               hist255 ++;
  932.  
  933.             if (*sort_ptr < white_level && *sort_ptr > black_level)
  934.               sort_ptr ++;
  935.           };
  936.  
  937.           /*
  938.            * Shell preview_sort the color values...
  939.            */
  940.  
  941.           sort_count = sort_ptr - preview_sort;
  942.  
  943.           if (sort_count > 1)
  944.         {
  945.           for (d = sort_count / 2; d > 0; d = d / 2)
  946.             for (i = d; i < sort_count; i ++)
  947.               for (j = i - d, sort_ptr = preview_sort + j;
  948.                j >= 0 && sort_ptr[0] > sort_ptr[d];
  949.                j -= d, sort_ptr -= d)
  950.             {
  951.               t           = sort_ptr[0];
  952.               sort_ptr[0] = sort_ptr[d];
  953.               sort_ptr[d] = t;
  954.             };
  955.  
  956.           /*
  957.            * Assign the median value...
  958.            */
  959.  
  960.           t = sort_count / 2;
  961.  
  962.           if (sort_count & 1)
  963.             *dst_ptr = (preview_sort[t] + preview_sort[t + 1]) / 2;
  964.           else
  965.             *dst_ptr = preview_sort[t];
  966.  
  967.           /*
  968.            * Save the change to the source image too if the user
  969.            * wants the recursive method...
  970.            */
  971.  
  972.           if (filter_type & FILTER_RECURSIVE)
  973.             preview_src[y * width + x] = *dst_ptr;
  974.         };
  975.  
  976.           /*
  977.            * Check the histogram and adjust the radius accordingly...
  978.            */
  979.  
  980.           if (filter_type & FILTER_ADAPTIVE)
  981.         {
  982.           if (hist0 >= radius || hist255 >= radius)
  983.             {
  984.               if (radius < despeckle_radius)
  985.             radius ++;
  986.             }
  987.           else if (radius > 1)
  988.             radius --;
  989.         };
  990.         };
  991.     };
  992.  
  993.       /*
  994.        * Draw this row...
  995.        */
  996.  
  997.       rgb_ptr = rgb + y * preview_width * 3;
  998.  
  999.       switch (img_bpp)
  1000.     {
  1001.     case 1:
  1002.           for (x = preview_width, dst_ptr = preview_dst;
  1003.                x > 0;
  1004.                x --, dst_ptr ++, rgb_ptr += 3)
  1005.             rgb_ptr[0] = rgb_ptr[1] = rgb_ptr[2] = *dst_ptr;
  1006.           break;
  1007.  
  1008.     case 2:
  1009.       for (x = preview_width, dst_ptr = preview_dst;
  1010.            x > 0;
  1011.            x --, dst_ptr += 2, rgb_ptr += 3)
  1012.         if (dst_ptr[1] == 255)
  1013.           rgb_ptr[0] = rgb_ptr[1] = rgb_ptr[2] = *dst_ptr;
  1014.         else
  1015.           {
  1016.         if ((y & GIMP_CHECK_SIZE) ^ (x & GIMP_CHECK_SIZE))
  1017.           check = GIMP_CHECK_LIGHT * 255;
  1018.         else
  1019.           check = GIMP_CHECK_DARK * 255;
  1020.  
  1021.         if (dst_ptr[1] == 0)
  1022.           rgb_ptr[0] = rgb_ptr[1] = rgb_ptr[2] = check;
  1023.         else
  1024.           rgb_ptr[0] = rgb_ptr[1] = rgb_ptr[2] =
  1025.                     check + ((dst_ptr[0] - check) * dst_ptr[1]) / 255;
  1026.           };
  1027.           break;
  1028.  
  1029.     case 3:
  1030.           memcpy (rgb_ptr, preview_dst, preview_width * 3);
  1031.           break;
  1032.  
  1033.     case 4:
  1034.       for (x = preview_width, dst_ptr = preview_dst;
  1035.            x > 0;
  1036.            x --, dst_ptr += 4, rgb_ptr += 3)
  1037.         if (dst_ptr[3] == 255)
  1038.           {
  1039.         rgb_ptr[0] = dst_ptr[0];
  1040.         rgb_ptr[1] = dst_ptr[1];
  1041.         rgb_ptr[2] = dst_ptr[2];
  1042.           }
  1043.         else
  1044.           {
  1045.         if ((y & GIMP_CHECK_SIZE) ^ (x & GIMP_CHECK_SIZE))
  1046.           check = GIMP_CHECK_LIGHT * 255;
  1047.         else
  1048.           check = GIMP_CHECK_DARK * 255;
  1049.  
  1050.         if (dst_ptr[3] == 0)
  1051.           rgb_ptr[0] = rgb_ptr[1] = rgb_ptr[2] = check;
  1052.         else
  1053.           {
  1054.             rgb_ptr[0] =
  1055.               check + ((dst_ptr[0] - check) * dst_ptr[3]) / 255;
  1056.             rgb_ptr[1] =
  1057.               check + ((dst_ptr[1] - check) * dst_ptr[3]) / 255;
  1058.             rgb_ptr[2] =
  1059.               check + ((dst_ptr[2] - check) * dst_ptr[3]) / 255;
  1060.           };
  1061.           };
  1062.           break;
  1063.     };
  1064.     };
  1065.  
  1066.   /*
  1067.    * Update the screen...
  1068.    */
  1069.  
  1070.   for (y = 0, rgb_ptr = rgb;
  1071.        y < preview_height;
  1072.        y ++, rgb_ptr += preview_width * 3)
  1073.     gtk_preview_draw_row (GTK_PREVIEW (preview), rgb_ptr, 0, y, preview_width);
  1074.  
  1075.   gtk_widget_draw (preview, NULL);
  1076.   gdk_flush ();
  1077. }
  1078.  
  1079. static void
  1080. preview_exit (void)
  1081. {
  1082.   g_free (preview_src);
  1083.   g_free (preview_dst);
  1084.   g_free (preview_sort);
  1085. }
  1086.  
  1087. static void
  1088. dialog_iscale_update (GtkAdjustment *adjustment,
  1089.               gint          *value)
  1090. {
  1091.   *value = adjustment->value;
  1092.  
  1093.   if (value == &despeckle_radius)
  1094.     preview_init ();
  1095.  
  1096.   preview_update ();
  1097. }
  1098.  
  1099. static void
  1100. dialog_adaptive_callback (GtkWidget *widget,
  1101.               gpointer   data)
  1102. {
  1103.   if (GTK_TOGGLE_BUTTON (widget)->active)
  1104.     filter_type |= FILTER_ADAPTIVE;
  1105.   else
  1106.     filter_type &= ~FILTER_ADAPTIVE;
  1107.  
  1108.   preview_update ();
  1109. }
  1110.  
  1111. static void
  1112. dialog_recursive_callback (GtkWidget *widget,
  1113.                gpointer  data)
  1114. {
  1115.   if (GTK_TOGGLE_BUTTON (widget)->active)
  1116.     filter_type |= FILTER_RECURSIVE;
  1117.   else
  1118.     filter_type &= ~FILTER_RECURSIVE;
  1119.  
  1120.   preview_update ();
  1121. }
  1122.  
  1123. static void
  1124. dialog_ok_callback (GtkWidget *widget,
  1125.             gpointer  data)
  1126. {
  1127.   run_filter = TRUE;
  1128.  
  1129.   gtk_widget_destroy (GTK_WIDGET (data));
  1130. }
  1131.